[PATCH 4/4] dnp3: bound the maximum number of objects per tx
authorJason Ish <jason.ish@oisf.net>
Tue, 6 Jan 2026 23:14:21 +0000 (17:14 -0600)
committerAndreas Dolp <dev@andreas-dolp.de>
Sun, 22 Feb 2026 12:28:52 +0000 (13:28 +0100)
Default to 2048, but provide a user configuration value.

Ticket: #8181
(cherry picked from commit 2c95f1ff44e17c3bc8693d5e23e175f2bc90ea10)

Origin: upstream, https://github.com/OISF/suricata/commit/a6d950315d9b6c1e35c10c24d9bb7128d422c21f.patch
Bug: https://redmine.openinfosecfoundation.org/issues/8181
Subject: Upstream fix for CVE-2026-22259 part 4

Gbp-Pq: Name CVE-2026-22259_4.patch

doc/userguide/upgrade.rst
rules/dnp3-events.rules
src/app-layer-dnp3.c
src/app-layer-dnp3.h
suricata.yaml.in

index 6c9903002b1312d5825475498e7e158e3e5b6216..fa1caa8107d9e89472426a4f0f74e75ac832312b 100644 (file)
@@ -40,10 +40,11 @@ Upgrading to 7.0.14 (trixie-security 1:7.0.10-1~bpo13u3)
 Other Changes
 ~~~~~~~~~~~~~
 - ``dnp3`` has reduced the maximum number of open transactions from
-  500 down to 32, and the maximum number of points per message from
-  unbounded to 16384. Configuration options, ``max-tx`` and
-  ``max-points`` have been added for users who may need to change
-  these defaults.
+  500 down to 32, the maximum number of points per message from
+  unbounded to 16384, and the maximum number of objects per message
+  from unbounded to 2048. Configuration options, ``max-tx``,
+  ``max-points``, and ``max-objects`` have been added for users who
+  may need to change these defaults.
 
 Upgrading to 7.0.9
 ------------------
index 59e4c24d293821835879cd723e6897bcbcc2bb4f..e3acdc3d0cef1e904000029b99191dbaf8dc82d1 100644 (file)
@@ -29,3 +29,8 @@ alert dnp3 any any -> any any (msg:"SURICATA DNP3 Unknown object"; \
 alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many points in message"; \
       app-layer-event:dnp3.too_many_points; \
       classtype:protocol-command-decode; sid:2270005; rev:1;)
+
+# Too many objects.
+alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many objects"; \
+      app-layer-event:dnp3.too_many_objects; \
+      classtype:protocol-command-decode; sid:2270006; rev:1;)
index b1ad323eec238fdf9f47b79f380fbe7f828a4913..daf6f0e06dff03ab9b13e4ace8c547dbff240ad0 100644 (file)
@@ -101,6 +101,9 @@ static uint64_t dnp3_max_tx = 32;
 /* The maximum number of points allowed per message (configurable). */
 static uint64_t max_points = 16384;
 
+/* The maximum number of objects allowed per message (configurable). */
+static uint64_t dnp3_max_objects = 2048;
+
 /* Decoder event map. */
 SCEnumCharMap dnp3_decoder_event_table[] = {
     { "FLOODED", DNP3_DECODER_EVENT_FLOODED },
@@ -110,6 +113,7 @@ SCEnumCharMap dnp3_decoder_event_table[] = {
     { "MALFORMED", DNP3_DECODER_EVENT_MALFORMED },
     { "UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT },
     { "TOO_MANY_POINTS", DNP3_DECODER_EVENT_TOO_MANY_POINTS },
+    { "TOO_MANY_OBJECTS", DNP3_DECODER_EVENT_TOO_MANY_OBJECTS },
     { NULL, -1 },
 };
 
@@ -714,6 +718,7 @@ static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf,
 {
     int retval = 0;
     uint64_t point_count = 0;
+    uint64_t object_count = 0;
 
     if (buf == NULL || len == 0) {
         return 1;
@@ -728,6 +733,12 @@ static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf,
         DNP3ObjHeader *header = (DNP3ObjHeader *)buf;
         offset += sizeof(DNP3ObjHeader);
 
+        /* Check if we've exceeded the maximum number of objects. */
+        if (++object_count > dnp3_max_objects) {
+            DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_OBJECTS);
+            goto done;
+        }
+
         DNP3Object *object = DNP3ObjectAlloc();
         if (unlikely(object == NULL)) {
             goto done;
@@ -1633,6 +1644,13 @@ void RegisterDNP3Parsers(void)
                 max_points = (uint64_t)value;
             }
         }
+
+        /* Parse max-objects configuration. */
+        if (ConfGetInt("app-layer.protocols.dnp3.max-objects", &value)) {
+            if (value > 0) {
+                dnp3_max_objects = (uint64_t)value;
+            }
+        }
     } else {
         SCLogConfig("Parser disabled for protocol %s. "
             "Protocol detection still on.", proto_name);
index 3e40c7ac662d99cd90fc82b098f9037bee40d4a2..2b99599b34dd933aa85aa3a1f0a906129fc75a7e 100644 (file)
@@ -110,6 +110,7 @@ enum {
     DNP3_DECODER_EVENT_MALFORMED,
     DNP3_DECODER_EVENT_UNKNOWN_OBJECT,
     DNP3_DECODER_EVENT_TOO_MANY_POINTS,
+    DNP3_DECODER_EVENT_TOO_MANY_OBJECTS,
 };
 
 /**
index 1514c01a5fc4497332662793ba76c9c95da62584..9cdc8243028960301fda7a05d943727746e40971 100644 (file)
@@ -1163,6 +1163,7 @@ app-layer:
         dp: 20000
       #max-tx: 32
       #max-points: 16384
+      #max-objects: 2048
 
     # SCADA EtherNet/IP and CIP protocol support
     enip: